package org.datascooter.meta;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import org.datascooter.bundle.DSMapper;
import org.datascooter.bundle.EntityBundle;
import org.datascooter.exception.DataScooterException;
import org.datascooter.impl.DataSnip;
import org.datascooter.impl.Snip;
import org.datascooter.inface.IDataManager;
import org.datascooter.inface.IReplicationManager;
import org.datascooter.utils.SnipUtils;
import org.datascooter.utils.policy.SnipType;

public class ReplicationManager implements IReplicationManager {

	private final IDataManager dataManager;

	public ReplicationManager(IDataManager dataManager) {
		this.dataManager = dataManager;
	}

	@Override
	public List<Snip> getReplica(TableReplica... repArray) {
		if (repArray.length == 0) {
			throw new DataScooterException("***Error replica's array is empty!");
		}
		List<Snip> result = new ArrayList<Snip>();
		for (TableReplica replica : repArray) {
			DataSnip snip = dataManager.startSnip(replica.entity, SnipType.SELECT);
			for (String string : replica.fieldArray) {
				snip.extraList.add(string);
			}
			result.add(dataManager.retrieve(snip));
		}
		return result;
	}

	@Override
	public void pipe(IDataManager source, String... entities) {
		for (int a = 0; a < entities.length; a++) {
			String entity = entities[a];
			int count = dataManager.snip(entity).count();
			if (count > 0) {
				throw new DataScooterException("***Table " + entity + "  are not empty please check it out or use setReplica method");
			}
			dataManager.executeAs(source.retrieve(new DataSnip(entity, SnipType.SELECT)), SnipType.INSERT);
		}
	}

	@Override
	public void pipeIn(List<Snip> listIn) {
		for (Snip snip : listIn) {
			try {
				int count = dataManager.snip(snip.entity).count();
				if (count > 0) {
					throw new DataScooterException("***Table " + snip.entity + "  are not empty please check it out or use setReplica method");
				}
				DataSnip dataSnip = SnipUtils.getSnipFromDataSet(snip.entity, snip.fields);
				dataSnip.setSnipType(SnipType.INSERT);
				Snip value = dataManager.getSnipCashe().getCachedSnip(dataSnip, dataManager.getTransactionId());
				value.setData(snip.getData());
				dataManager.execute(value);
			} catch (Exception e) {
				throw new DataScooterException(e);
			}
		}
	}

	@Override
	public void pipeInAll(List<Snip> listIn) {
		for (Snip snip : listIn) {
			try {
				dataManager.delete(snip.entity);
				if (snip.getData() != null && snip.getData().length > 0) {
					DataSnip dataSnip = SnipUtils.getSnipFromDataSet(snip.entity, snip.fields);
					dataSnip.setSnipType(SnipType.INSERT);
					Snip value = dataManager.getSnipCashe().getCachedSnip(dataSnip, dataManager.getTransactionId());
					value.setData(snip.getData());
					dataManager.execute(value);
				}
			} catch (Exception e) {
				throw new DataScooterException(e);
			}
		}
	}

	@Override
	public void setReplica(List<Snip> listIn) {
		for (Snip snip : listIn) {
			try {
				EntityBundle bundle = DSMapper.getBundle(snip.entity);
				if (bundle.id != null) {
					DataSnip dataSnip = new DataSnip(snip.entity, SnipType.SELECT);
					dataSnip.extraList.add(bundle.id.name);
					Snip retrieve = dataManager.retrieve(dataSnip);
					HashSet<Object> inSet = new HashSet<Object>();
					HashSet<Object> exSet = new HashSet<Object>();
					for (int b = 0; b < retrieve.getData().length; b++) {
						inSet.add(retrieve.getData()[b][0]);
					}
					for (int b = 0; b < snip.getData().length; b++) {
						exSet.add(snip.getData()[b][0]);
					}
					inSet.removeAll(exSet);
					if (inSet.size() > 0) {
						dataSnip = new DataSnip(snip.entity, SnipType.DELETE);
						dataSnip.whereId(-1);
						Snip value = dataManager.getSnipCashe().getCachedSnip(dataSnip, dataManager.getTransactionId());
						Object[][] array = new Object[inSet.size()][1];
						int b = 0;
						for (Object object : inSet) {
							array[b++][0] = object;
						}
						value.setData(array);
						dataManager.execute(value);
					}
					inSet.clear();
					for (int b = 0; b < retrieve.getData().length; b++) {
						inSet.add(retrieve.getData()[b][0]);
					}
					inSet.retainAll(exSet);
					Object[][] updateArray = new Object[inSet.size()][];
					Object[][] saveArray = new Object[exSet.size() - inSet.size()][];
					int updIndex = 0;
					int saveIndex = 0;
					for (int b = 0; b < snip.getData().length; b++) {
						if (inSet.contains(snip.getData()[b][0])) {
							updateArray[updIndex++] = snip.getData()[b];
						} else {
							saveArray[saveIndex++] = snip.getData()[b];
						}
					}
					if (updateArray.length > 0) {
						dataSnip = SnipUtils.getSnipFromDataSet(snip.entity, snip.fields);
						dataSnip.setSnipType(SnipType.UPDATE);
						dataSnip.whereId(-1);
						Snip value = dataManager.getSnipCashe().getCachedSnip(dataSnip, dataManager.getTransactionId());
						value.setData(updateArray);
						dataManager.execute(value);
					}
					if (saveArray.length > 0) {
						dataSnip = SnipUtils.getSnipFromDataSet(snip.entity, snip.fields);
						dataSnip.setSnipType(SnipType.INSERT);
						Snip value = dataManager.getSnipCashe().getCachedSnip(dataSnip, dataManager.getTransactionId());
						value.setData(saveArray);
						dataManager.execute(value);
					}
				} else {
					DataSnip dataSnip = SnipUtils.getSnipFromDataSet(snip.entity, snip.fields);
					dataSnip.setSnipType(SnipType.INSERT);
					Snip value = dataManager.getSnipCashe().getCachedSnip(dataSnip, dataManager.getTransactionId());
					value.setData(snip.getData());
					dataManager.execute(value);
				}
			} catch (Exception e) {
				throw new DataScooterException(e);
			}
		}
	}
}
